package automaton;

import java.util.Iterator;
import java.util.LinkedList;

public abstract class AbstractConfiguration implements Runnable{
	
	private Thread thread;
	private Manager manager;
	private Automaton aut;
	private State state;
	private String input;
	private String output;
	private boolean switchedOff;
	
	public AbstractConfiguration(Manager manager, Automaton aut, State state, String input, String output){
		this.manager = manager;
		this.aut = aut;
		this.state = state;
		this.input = input;
		this.output = output;
		this.thread = new Thread(this);
		this.switchedOff = true;
	}
	
	public Automaton getAut() {
		return aut;
	}

	public void setAut(Automaton aut) {
		this.aut = aut;
	}

	public State getState() {
		return state;
	}

	public void setState(State state) {
		this.state = state;
	}

	public String getOutput() {
		return output;
	}

	public void setOutput(String output) {
		this.output = output;
	}

	private void step(){		
		if(this.input.length() == 0){
			this.manager.fail(this);
		} else {
			char nextChar = this.input.charAt(0);
			this.input = this.input.substring(1, this.input.length());
			LinkedList<State> nextStates = this.getStatesAfterStep(nextChar);
			if(nextStates.size() == 0){
				this.manager.fail(this);
			} else {	
				this.createNewConfigsAndContinue(nextStates);
			}
		}
	}
	
	private void createNewConfigsAndContinue(LinkedList<State> nextStates){
		State nextState = nextStates.removeFirst();
		Iterator<State> i = nextStates.iterator();
		
		while(i.hasNext()){
			State current = i.next();
			String outputCharToCurrent = ""+this.getOutputAfter(current);
			this.manager.newConfig(this.aut, current, this.input, outputCharToCurrent, this,  this.output.length());
		}
		
		char outputCharToNextState = this.getOutputAfter(nextState);
		this.output = this.output + outputCharToNextState;
		this.setState(nextState);
	}
	
	private Character getOutputAfter(State newState) {
		for (StateTransition current : this.state.getTransitions()){
			if(current.getTo().equals(newState)) return current.getOutput();
		}
		throw new Error("not possible");
	}

	private LinkedList<State> getStatesAfterStep(char nextChar){
		LinkedList<State> resultStates =  new LinkedList<State>();
		Iterator<StateTransition> i = this.state.getTransitions().iterator();
		while(i.hasNext()){
			StateTransition current = i.next();
			if(current.getBy() == nextChar) {
				resultStates.add(current.getTo());
			}
		}
		return resultStates;
		
	}
	
	public void start(){
		this.switchedOff = false;
		this.thread.start();
	}
	public void stop(){
		this.switchedOff = true;
	}
	
	public void run() {
		while(!switchedOff){
			if(this.state.equals(this.aut.getEndState()) && this.input.length() == 0) {
				this.manager.calculateResult(this);
			} else {
			this.step();
			}
		}
	}
	
	public Thread getThread(){
		return this.thread;
	}
	
	public abstract String getOutputStringFromPrevious();
	
	public String getCompleteOutputString(){
		String result = this.getOutputStringFromPrevious() + this.getOutput();
		return result;
	}

}
